/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_SCENEMANAGER_H
#define M_SCENEMANAGER_H

#include "scenenode.h"
#include "signal.h"
#include "entity.h"
#include "refptr.h"
#include "color.h"
#include "light.h"
#include "renderqueue.h"
#include "types.h"
#include "particlesystem.h"
#include "threadpool.h"

#include <string>
#include <map>
#include <vector>

namespace Engine
{
	class Camera;
	class Graphic;
	class RenderTarget;

	/**
	*   Class for rendering shadow filling
	*/
	class ShadowFill: public Renderable
	{
	public:
		ShadowFill();
		~ShadowFill();
		void Update(const Camera* camera);
		virtual const MaterialPtr GetMaterial(void) const;
		virtual RenderDataPtr GetRenderData() const;
		virtual const Matrix4<REAL>& GetWorldTransform() const;
		virtual const HardWareBufferPtr GetVertexData() const;
		virtual const HardWareBufferPtr GetIndexData() const;
		virtual const VertexDeclarationPtr GetVertexDeclaration() const ;
	private:
		Matrix4<REAL> m_transform;
		MaterialPtr m_material;
		RefPtr<RenderData, WeakPtrExtension> m_renderData;
		RefPtr<VertexDeclaration,WeakPtrExtension> m_vertexDeclaration;
		RefPtr<HardWareBuffer,WeakPtrExtension> m_vertexData;
		RefPtr<HardWareBuffer,WeakPtrExtension> m_indexData;
	};

	/**
	*	Manages the organization and rendering of a 'scene' i.e. a collection
	*	of objects and potentially world geometry.
	*/
	class SceneManager
	{
	public:
		friend class Graphic;

		typedef std::map<std::string, RefPtr<Camera, WeakPtrExtension> > CameraMap;
		typedef std::map<std::string, RefPtr<MovableObject, WeakPtrExtension> > MovableObjectMap;
		typedef std::vector<PhysicSystemPtr> PhysicSystemsVector;

	public:

		/**
		*   Constructor.
		*/
		SceneManager(const std::string& name);

		/**
		*   Destructor.
		*/
		virtual ~SceneManager();

		/**
		*   Return objects name.
		*/
		const std::string& GetName(){return m_name;}

		/**
		*   Send the contents of the scene it manages to the rendering
        *   pipeline, possibly preceded by some sorting, culling
        *   or other scene management tasks.
		*/
		virtual void RenderScene(const Camera* camera,RenderTarget* renderTarget);

		/**
		*   Create camera associated with this scene manager.
		*/
		CameraPtr CreateCamera(const std::string& name);

		/**
		*   Return root scene node.
		*/
		SceneNodePtr GetRootSceneNode();

		/**
		*   Go through scene graph and update all nodes.
		*   Calculate world positions of all nodes.
		*/
		virtual void UpdateSceneGraph();

		/**
		*   Searches for visible objects and populate render queue.
		*/
		virtual void FindVisibleObjects(const Camera* camera);

		/**
		*   Render content of current render queue.
		*/
		void RenderVisibleObjects(const Camera* camera);

		/**
		*   Set scene ambient light.
		*/
		void SetAmbientLight(const Color& color);

		/**
		*   Create light object
		*   @param name lights name.
		*/
		LightPtr CreateLight(const std::string& name);

		/**
		*   Delete all lights objects
		*/
		void DeleteAllLights();

		/**
		*   Create mesh instance - entity
		*   @param name entity name.
		*   @param file name of mesh file resource.
		*/
		EntityPtr CreateEntity(const std::string& name, const std::string& file);

		/**
		*   Delete all entities
		*/
		void DeleteAllEntities();

		/**
		*   Create ParticleSystem instance.
		*   @param name ParticleSystem name.
		*   @param file material file resource.
		*   @param count maximum number of particles.
		*/
		ParticleSystemPtr CreateParticleSystem(const std::string& name, const std::string& material, unsigned int count);

		/**
		*   Delete all ParticleSystems.
		*/
		void DeleteAllParticleSystems();

		/**
		*   Delete movable object instance - light, entity, particle system
		*   @param name movable object name.
		*/
		void DeleteMovableObject(const std::string& name);

		/**
		*   Fired when frame is rendering.
		*/
		Signal<> RenderSignal;

		/**
		*   Add mesh to render as static batched geometry.
		*/
		virtual void AddStaticGeometry(const std::string& fname, const Matrix4<REAL>& transform){}

		/**
		*   Create minimal renderables for added static geometry.
		*/
		virtual void BuildStaticGeometry(){}

		/**
		*   Delete static geometry and scene culling data.
		*/
		virtual void ResetScene(){};

		/**
		*   Associate physic system with scene manager.
		*   Physic will create bodies as nodes of this manager.
		*/
		void AddPhysicSystem(PhysicSystemPtr physic);

	protected:

		/**
		*   Set new root node
		*/
		void SetRootSceneNode(RefPtr<SceneNode, WeakPtrExtension> node){m_rootSceneNode = node;}

		/**
		*   Return current render queue
		*/
		RenderQueue* GetRenderQueue(){return &m_renderQueue;}

		RenderQueue* GetShadowRenderQueue(){return &m_shadowsRenderQueue;};

		const std::vector<LightPtr>& GetLightList()const {return m_lightList;}

	private:

		class ParticlesUpdateTask: public BaseTask
		{
		public:
			ParticlesUpdateTask():ps(0){}
			void Run();
			std::vector<ParticleSystemPtr>* ps;
		};

		class PhysicsUpdateTask: public BaseTask
		{
		public:
			PhysicsUpdateTask():physics(0){}
			void Run();
			PhysicSystemsVector* physics;
		};

		class ClearScreenTask: public BaseTask
		{
		public:
			ClearScreenTask():renderTarget(0){}
			void Run();
			RenderTarget* renderTarget;
		};
	private:
		/**
		*   Render one object.
		*/
		void RenderSingleObject(const RenderablePtr rend);

		/**
		*   Render objects.
		*/
		void RenderObjects(const RenderQueue::RenderableSortGroupVector& items);

		/**
		*   Create movable object with name of specified type.
		*/
		RefPtr<MovableObject, WeakPtrExtension>  CreateMovableObject(const std::string& name, const std::string& type, const NamedParameters* parameters = NULL);

		/**
		*   Delete group of movable objects.
		*/
		void DeleteMovableObject(MovableObjectVisitor* visitor);

		/**
		*   Constructor.
		*/
		SceneManager();

		/**
		*   Render stencil shadows
		*/
		void RenderShadows(const Camera* camera);

	private:

		std::string m_name;
		CameraMap m_cameras;

		Color m_ambientLight;

		RefPtr<SceneNode, WeakPtrExtension> m_rootSceneNode;

		MovableObjectMap m_movObjects;

		RenderQueue m_renderQueue;

		RenderQueue m_shadowsRenderQueue;

		std::vector<LightPtr> m_lightList;
		std::vector<ParticleSystemPtr> m_particleList;
		PhysicSystemsVector m_physicSystems;

		RefPtr<ShadowFill, WeakPtrExtension> m_shadowFill;

		RefPtr<BaseTask> m_particlesUpdateTask;
		RefPtr<BaseTask> m_physicsUpdateTask;
		RefPtr<BaseTask> m_clearScreenTask;
	};

	/**
	*   Factory for creating scene manager objects.
	*/
	class SceneManagerFactory: public Factory<SceneManager>
	{
	public:

		SceneManagerFactory():m_type("Default"){}

		/**
		*   Returns the factory type.
		*/
		virtual const std::string& GetType() const {return m_type;}

		/**
		*   Creates a new object.
		*   @param name Name of the object to create
		*   @return	An object created by the factory. The type of the object depends on	the factory.
		*/
		virtual SceneManager* CreateInstance( const std::string& name);

	private:

		std::string m_type;
	};

}

#endif

